const std = @import("std");
const c = @cImport({
    @cInclude("uv.h");
});

const zeroes = std.mem.zeroes;

const Context = struct {
    count: u64 = 0,
};

// convert libuv result into error set
fn check(res: isize) !void {
    if (res >= 0) return;

    return switch (res) {
        c.UV_EOF => error.EndOfFile,
        // ---SNIP---
        else => error.UnknownError,
    };
}

fn counter(timer: ?*c.uv_timer_t) callconv(.C) void {
    // the `data` field is void pointer so we need to cast it
    const ctx: *Context = @ptrCast(@alignCast(timer.?.data));

    std.debug.print("counter: {d}\n", .{ctx.count});

    ctx.count += 1;
    if (ctx.count >= 10) {
        _ = c.uv_timer_stop(timer.?);
        c.uv_close(@ptrCast(timer.?), null);
    }
}

pub fn main() !void {
    // initialize the libuv event loop
    const loop = c.uv_default_loop();
    defer _ = c.uv_loop_close(loop);

    // equivalent to C's `uv_timer_t timer = {0};`
    var timer = zeroes(c.uv_timer_t);

    // initialize the timer
    try check(c.uv_timer_init(loop, &timer));

    // create context
    var ctx = Context{};
    timer.data = &ctx;

    // start the timer and pass our callback to it
    try check(c.uv_timer_start(&timer, counter, 0, 100));

    // run the loop
    try check(c.uv_run(loop, c.UV_RUN_DEFAULT));
}
